<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#字符串的扩展"  class="anchor" id="user-content-字符串的扩展">

    </a>字符串的扩展
    </h1>
    <p>ES6 加强了对 Unicode 的支持，并且扩展了字符串对象。</p>
    <h2><a href="#字符的-unicode-表示法"  class="anchor" id="user-content-字符的-unicode-表示法">

    </a>字符的 Unicode 表示法
    </h2>
    <p>JavaScript 允许采用<code>\uxxxx</code>形式表示一个字符，其中<code>xxxx</code>表示字符的 Unicode 码点。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">"</span><span
            class="pl-cce">\u0061</span><span class="pl-pds">"</span></span>
<span class="pl-c"><span class="pl-c">//</span> "a"</span></pre>
    </div>
    <p>但是，这种表示法只限于码点在<code>\u0000</code>~<code>\uFFFF</code>之间的字符。超出这个范围的字符，必须用两个双字节的形式表示。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">"</span><span
            class="pl-cce">\uD842\uDFB7</span><span class="pl-pds">"</span></span>
<span class="pl-c"><span class="pl-c">//</span> "𠮷"</span>

<span class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\u20BB</span>7<span class="pl-pds">"</span></span>
<span class="pl-c"><span class="pl-c">//</span> " 7"</span></pre>
    </div>
    <p>上面代码表示，如果直接在<code>\u</code>后面跟上超过<code>0xFFFF</code>的数值（比如<code>\u20BB7</code>），JavaScript
        会理解成<code>\u20BB+7</code>。由于<code>\u20BB</code>是一个不可打印字符，所以只会显示一个空格，后面跟着一个<code>7</code>。</p>
    <p>ES6 对这一点做出了改进，只要将码点放入大括号，就能正确解读该字符。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">"</span><span
            class="pl-cce">\u{20BB7}</span><span class="pl-pds">"</span></span>
<span class="pl-c"><span class="pl-c">//</span> "𠮷"</span>

<span class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\u{41}\u{42}\u{43}</span><span
        class="pl-pds">"</span></span>
<span class="pl-c"><span class="pl-c">//</span> "ABC"</span>

<span class="pl-k">let</span> hello <span class="pl-k">=</span> <span class="pl-c1">123</span>;
hell\u{<span class="pl-ii">6F</span>} <span class="pl-c"><span class="pl-c">//</span> 123</span>

<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\u{1F680}</span><span
        class="pl-pds">'</span></span> <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span><span class="pl-cce">\uD83D\uDE80</span><span class="pl-pds">'</span></span>
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，最后一个例子表明，大括号表示法与四字节的 UTF-16 编码是等价的。</p>
    <p>有了这种表示法之后，JavaScript 共有 6 种方法可以表示一个字符。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span><span
            class="pl-cce">\z</span><span class="pl-pds">'</span></span> <span class="pl-k">===</span> <span
            class="pl-s"><span class="pl-pds">'</span>z<span class="pl-pds">'</span></span>  <span class="pl-c"><span
            class="pl-c">//</span> true</span>
<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\172</span><span
        class="pl-pds">'</span></span> <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span>z<span class="pl-pds">'</span></span> <span class="pl-c"><span
                class="pl-c">//</span> true</span>
<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\x7A</span><span
        class="pl-pds">'</span></span> <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span>z<span class="pl-pds">'</span></span> <span class="pl-c"><span
                class="pl-c">//</span> true</span>
<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\u007A</span><span
        class="pl-pds">'</span></span> <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span>z<span class="pl-pds">'</span></span> <span class="pl-c"><span
                class="pl-c">//</span> true</span>
<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\u{7A}</span><span
        class="pl-pds">'</span></span> <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span>z<span class="pl-pds">'</span></span> <span class="pl-c"><span
                class="pl-c">//</span> true</span></pre>
    </div>
    <h2><a href="#codepointat"  class="anchor" id="user-content-codepointat">

    </a>codePointAt()
    </h2>
    <p>JavaScript 内部，字符以 UTF-16 的格式储存，每个字符固定为<code>2</code>个字节。对于那些需要<code>4</code>个字节储存的字符（Unicode
        码点大于<code>0xFFFF</code>的字符），JavaScript 会认为它们是两个字符。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> s <span class="pl-k">=</span> <span
            class="pl-s"><span class="pl-pds">"</span>𠮷<span class="pl-pds">"</span></span>;

<span class="pl-smi">s</span>.<span class="pl-c1">length</span> <span class="pl-c"><span class="pl-c">//</span> 2</span>
<span class="pl-smi">s</span>.<span class="pl-c1">charAt</span>(<span class="pl-c1">0</span>) <span class="pl-c"><span
                class="pl-c">//</span> ''</span>
<span class="pl-smi">s</span>.<span class="pl-c1">charAt</span>(<span class="pl-c1">1</span>) <span class="pl-c"><span
                class="pl-c">//</span> ''</span>
<span class="pl-smi">s</span>.<span class="pl-c1">charCodeAt</span>(<span class="pl-c1">0</span>) <span
                class="pl-c"><span class="pl-c">//</span> 55362</span>
<span class="pl-smi">s</span>.<span class="pl-c1">charCodeAt</span>(<span class="pl-c1">1</span>) <span
                class="pl-c"><span class="pl-c">//</span> 57271</span></pre>
    </div>
    <p>上面代码中，汉字“𠮷”（注意，这个字不是“吉祥”的“吉”）的码点是<code>0x20BB7</code>，UTF-16 编码为<code>0xD842 0xDFB7</code>（十进制为<code>55362
        57271</code>），需要<code>4</code>个字节储存。对于这种<code>4</code>个字节的字符，JavaScript 不能正确处理，字符串长度会误判为<code>2</code>，而且<code>charAt</code>方法无法读取整个字符，<code>charCodeAt</code>方法只能分别返回前两个字节和后两个字节的值。
    </p>
    <p>ES6 提供了<code>codePointAt</code>方法，能够正确处理 4 个字节储存的字符，返回一个字符的码点。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s <span class="pl-k">=</span> <span
            class="pl-s"><span class="pl-pds">'</span>𠮷a<span class="pl-pds">'</span></span>;

<span class="pl-smi">s</span>.<span class="pl-en">codePointAt</span>(<span class="pl-c1">0</span>) <span
                class="pl-c"><span class="pl-c">//</span> 134071</span>
<span class="pl-smi">s</span>.<span class="pl-en">codePointAt</span>(<span class="pl-c1">1</span>) <span
                class="pl-c"><span class="pl-c">//</span> 57271</span>

<span class="pl-smi">s</span>.<span class="pl-en">codePointAt</span>(<span class="pl-c1">2</span>) <span
                class="pl-c"><span class="pl-c">//</span> 97</span></pre>
    </div>
    <p><code>codePointAt</code>方法的参数，是字符在字符串中的位置（从 0 开始）。上面代码中，JavaScript 将“𠮷a”视为三个字符，codePointAt
        方法在第一个字符上，正确地识别了“𠮷”，返回了它的十进制码点
        134071（即十六进制的<code>20BB7</code>）。在第二个字符（即“𠮷”的后两个字节）和第三个字符“a”上，<code>codePointAt</code>方法的结果与<code>charCodeAt</code>方法相同。
    </p>
    <p>总之，<code>codePointAt</code>方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符，它的返回结果与<code>charCodeAt</code>方法相同。</p>
    <p><code>codePointAt</code>方法返回的是码点的十进制值，如果想要十六进制的值，可以使用<code>toString</code>方法转换一下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s <span class="pl-k">=</span> <span
            class="pl-s"><span class="pl-pds">'</span>𠮷a<span class="pl-pds">'</span></span>;

<span class="pl-smi">s</span>.<span class="pl-en">codePointAt</span>(<span class="pl-c1">0</span>).<span class="pl-c1">toString</span>(<span
                class="pl-c1">16</span>) <span class="pl-c"><span class="pl-c">//</span> "20bb7"</span>
<span class="pl-smi">s</span>.<span class="pl-en">codePointAt</span>(<span class="pl-c1">2</span>).<span class="pl-c1">toString</span>(<span
                class="pl-c1">16</span>) <span class="pl-c"><span class="pl-c">//</span> "61"</span></pre>
    </div>
    <p>你可能注意到了，<code>codePointAt</code>方法的参数，仍然是不正确的。比如，上面代码中，字符<code>a</code>在字符串<code>s</code>的正确位置序号应该是 1，但是必须向<code>codePointAt</code>方法传入
        2。解决这个问题的一个办法是使用<code>for...of</code>循环，因为它会正确识别 32 位的 UTF-16 字符。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s <span class="pl-k">=</span> <span
            class="pl-s"><span class="pl-pds">'</span>𠮷a<span class="pl-pds">'</span></span>;
<span class="pl-k">for</span> (<span class="pl-k">let</span> ch <span class="pl-k">of</span> s) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">ch</span>.<span class="pl-en">codePointAt</span>(<span
                class="pl-c1">0</span>).<span class="pl-c1">toString</span>(<span class="pl-c1">16</span>));
}
<span class="pl-c"><span class="pl-c">//</span> 20bb7</span>
<span class="pl-c"><span class="pl-c">//</span> 61</span></pre>
    </div>
    <p><code>codePointAt</code>方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">is32Bit</span>(<span class="pl-smi">c</span>) {
  <span class="pl-k">return</span> <span class="pl-smi">c</span>.<span class="pl-en">codePointAt</span>(<span
                class="pl-c1">0</span>) <span class="pl-k">&gt;</span> <span class="pl-c1">0xFFFF</span>;
}

<span class="pl-en">is32Bit</span>(<span class="pl-s"><span class="pl-pds">"</span>𠮷<span
                class="pl-pds">"</span></span>) <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-en">is32Bit</span>(<span class="pl-s"><span class="pl-pds">"</span>a<span class="pl-pds">"</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> false</span></pre>
    </div>
    <h2><a href="#stringfromcodepoint"  class="anchor" id="user-content-stringfromcodepoint">

    </a>String.fromCodePoint()
    </h2>
    <p>ES5 提供<code>String.fromCharCode</code>方法，用于从码点返回对应字符，但是这个方法不能识别 32 位的 UTF-16 字符（Unicode 编号大于<code>0xFFFF</code>）。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-c1">fromCharCode</span>(<span class="pl-c1">0x20BB7</span>)
<span class="pl-c"><span class="pl-c">//</span> "ஷ"</span></pre>
    </div>
    <p>上面代码中，<code>String.fromCharCode</code>不能识别大于<code>0xFFFF</code>的码点，所以<code>0x20BB7</code>就发生了溢出，最高位<code>2</code>被舍弃了，最后返回码点<code>U+0BB7</code>对应的字符，而不是码点<code>U+20BB7</code>对应的字符。
    </p>
    <p>ES6 提供了<code>String.fromCodePoint</code>方法，可以识别大于<code>0xFFFF</code>的字符，弥补了<code>String.fromCharCode</code>方法的不足。在作用上，正好与<code>codePointAt</code>方法相反。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-en">fromCodePoint</span>(<span class="pl-c1">0x20BB7</span>)
<span class="pl-c"><span class="pl-c">//</span> "𠮷"</span>
<span class="pl-c1">String</span>.<span class="pl-en">fromCodePoint</span>(<span class="pl-c1">0x78</span>, <span
                class="pl-c1">0x1f680</span>, <span class="pl-c1">0x79</span>) <span class="pl-k">===</span> <span
                class="pl-s"><span class="pl-pds">'</span>x<span class="pl-cce">\uD83D\uDE80</span>y<span
                class="pl-pds">'</span></span>
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，如果<code>String.fromCodePoint</code>方法有多个参数，则它们会被合并成一个字符串返回。</p>
    <p>注意，<code>fromCodePoint</code>方法定义在<code>String</code>对象上，而<code>codePointAt</code>方法定义在字符串的实例对象上。</p>
    <h2><a href="#字符串的遍历器接口"  class="anchor" id="user-content-字符串的遍历器接口">

    </a>字符串的遍历器接口
    </h2>
    <p>ES6 为字符串添加了遍历器接口（详见《Iterator》一章），使得字符串可以被<code>for...of</code>循环遍历。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">for</span> (<span class="pl-k">let</span> codePoint <span
            class="pl-k">of</span> <span class="pl-s"><span class="pl-pds">'</span>foo<span
            class="pl-pds">'</span></span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(codePoint)
}
<span class="pl-c"><span class="pl-c">//</span> "f"</span>
<span class="pl-c"><span class="pl-c">//</span> "o"</span>
<span class="pl-c"><span class="pl-c">//</span> "o"</span></pre>
    </div>
    <p>除了遍历字符串，这个遍历器最大的优点是可以识别大于<code>0xFFFF</code>的码点，传统的<code>for</code>循环无法识别这样的码点。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> text <span class="pl-k">=</span> <span
            class="pl-c1">String</span>.<span class="pl-en">fromCodePoint</span>(<span class="pl-c1">0x20BB7</span>);

<span class="pl-k">for</span> (<span class="pl-k">let</span> i <span class="pl-k">=</span> <span class="pl-c1">0</span>; i <span
                class="pl-k">&lt;</span> <span class="pl-smi">text</span>.<span class="pl-c1">length</span>; i<span
                class="pl-k">++</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(text[i]);
}
<span class="pl-c"><span class="pl-c">//</span> " "</span>
<span class="pl-c"><span class="pl-c">//</span> " "</span>

<span class="pl-k">for</span> (<span class="pl-k">let</span> i <span class="pl-k">of</span> text) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(i);
}
<span class="pl-c"><span class="pl-c">//</span> "𠮷"</span></pre>
    </div>
    <p>上面代码中，字符串<code>text</code>只有一个字符，但是<code>for</code>循环会认为它包含两个字符（都不可打印），而<code>for...of</code>循环会正确识别出这一个字符。</p>
    <h2><a href="#at"  class="anchor" id="user-content-at">

    </a>at()
    </h2>
    <p>ES5 对字符串对象提供<code>charAt</code>方法，返回字符串给定位置的字符。该方法不能识别码点大于<code>0xFFFF</code>的字符。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>abc<span
            class="pl-pds">'</span></span>.<span class="pl-c1">charAt</span>(<span class="pl-c1">0</span>) <span
            class="pl-c"><span class="pl-c">//</span> "a"</span>
<span class="pl-s"><span class="pl-pds">'</span>𠮷<span class="pl-pds">'</span></span>.<span class="pl-c1">charAt</span>(<span
                class="pl-c1">0</span>) <span class="pl-c"><span class="pl-c">//</span> "\uD842"</span></pre>
    </div>
    <p>上面代码中，<code>charAt</code>方法返回的是 UTF-16 编码的第一个字节，实际上是无法显示的。</p>
    <p>目前，有一个提案，提出字符串实例的<code>at</code>方法，可以识别 Unicode 编号大于<code>0xFFFF</code>的字符，返回正确的字符。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>abc<span
            class="pl-pds">'</span></span>.<span class="pl-en">at</span>(<span class="pl-c1">0</span>) <span
            class="pl-c"><span class="pl-c">//</span> "a"</span>
<span class="pl-s"><span class="pl-pds">'</span>𠮷<span class="pl-pds">'</span></span>.<span
                class="pl-en">at</span>(<span class="pl-c1">0</span>) <span class="pl-c"><span class="pl-c">//</span> "𠮷"</span></pre>
    </div>
    <p>这个方法可以通过<a href="https://github.com/es-shims/String.prototype.at">垫片库</a>实现。</p>
    <h2><a href="#normalize"  class="anchor" id="user-content-normalize">

    </a>normalize()
    </h2>
    <p>许多欧洲语言有语调符号和重音符号。为了表示它们，Unicode 提供了两种方法。一种是直接提供带重音符号的字符，比如<code>Ǒ</code>（\u01D1）。另一种是提供合成符号（combining
        character），即原字符与重音符号的合成，两个字符合成一个字符，比如<code>O</code>（\u004F）和<code>ˇ</code>（\u030C）合成<code>Ǒ</code>（\u004F\u030C）。
    </p>
    <p>这两种表示方法，在视觉和语义上都等价，但是 JavaScript 不能识别。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span><span
            class="pl-cce">\u01D1</span><span class="pl-pds">'</span></span><span class="pl-k">===</span><span
            class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\u004F\u030C</span><span
            class="pl-pds">'</span></span> <span class="pl-c"><span class="pl-c">//</span>false</span>

<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\u01D1</span><span
        class="pl-pds">'</span></span>.<span class="pl-c1">length</span> <span class="pl-c"><span class="pl-c">//</span> 1</span>
<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\u004F\u030C</span><span
        class="pl-pds">'</span></span>.<span class="pl-c1">length</span> <span class="pl-c"><span class="pl-c">//</span> 2</span></pre>
    </div>
    <p>上面代码表示，JavaScript 将合成字符视为两个字符，导致两种表示方法不相等。</p>
    <p>ES6 提供字符串实例的<code>normalize()</code>方法，用来将字符的不同表示方法统一为同样的形式，这称为 Unicode 正规化。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span><span
            class="pl-cce">\u01D1</span><span class="pl-pds">'</span></span>.<span
            class="pl-c1">normalize</span>() <span class="pl-k">===</span> <span class="pl-s"><span
            class="pl-pds">'</span><span class="pl-cce">\u004F\u030C</span><span class="pl-pds">'</span></span>.<span
            class="pl-c1">normalize</span>()
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p><code>normalize</code>方法可以接受一个参数来指定<code>normalize</code>的方式，参数的四个可选值如下。</p>
    <ul>
        <li><code>NFC</code>，默认参数，表示“标准等价合成”（Normalization Form Canonical
            Composition），返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。
        </li>
        <li><code>NFD</code>，表示“标准等价分解”（Normalization Form Canonical Decomposition），即在标准等价的前提下，返回合成字符分解的多个简单字符。</li>
        <li><code>NFKC</code>，表示“兼容等价合成”（Normalization Form Compatibility
            Composition），返回合成字符。所谓“兼容等价”指的是语义上存在等价，但视觉上不等价，比如“囍”和“喜喜”。（这只是用来举例，<code>normalize</code>方法不能识别中文。）
        </li>
        <li><code>NFKD</code>，表示“兼容等价分解”（Normalization Form Compatibility Decomposition），即在兼容等价的前提下，返回合成字符分解的多个简单字符。
        </li>
    </ul>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span><span
            class="pl-cce">\u004F\u030C</span><span class="pl-pds">'</span></span>.<span class="pl-c1">normalize</span>(<span
            class="pl-s"><span class="pl-pds">'</span>NFC<span class="pl-pds">'</span></span>).<span class="pl-c1">length</span> <span
            class="pl-c"><span class="pl-c">//</span> 1</span>
<span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\u004F\u030C</span><span
        class="pl-pds">'</span></span>.<span class="pl-c1">normalize</span>(<span class="pl-s"><span
                class="pl-pds">'</span>NFD<span class="pl-pds">'</span></span>).<span class="pl-c1">length</span> <span
                class="pl-c"><span class="pl-c">//</span> 2</span></pre>
    </div>
    <p>上面代码表示，<code>NFC</code>参数返回字符的合成形式，<code>NFD</code>参数返回字符的分解形式。</p>
    <p>不过，<code>normalize</code>方法目前不能识别三个或三个以上字符的合成。这种情况下，还是只能使用正则表达式，通过 Unicode 编号区间判断。</p>
    <h2><a href="#includes-startswith-endswith"  class="anchor"
           id="user-content-includes-startswith-endswith">

    </a>includes(), startsWith(), endsWith()
    </h2>
    <p>传统上，JavaScript 只有<code>indexOf</code>方法，可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。</p>
    <ul>
        <li><strong>includes()</strong>：返回布尔值，表示是否找到了参数字符串。</li>
        <li><strong>startsWith()</strong>：返回布尔值，表示参数字符串是否在原字符串的头部。</li>
        <li><strong>endsWith()</strong>：返回布尔值，表示参数字符串是否在原字符串的尾部。</li>
    </ul>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s <span class="pl-k">=</span> <span
            class="pl-s"><span class="pl-pds">'</span>Hello world!<span class="pl-pds">'</span></span>;

<span class="pl-smi">s</span>.<span class="pl-en">startsWith</span>(<span class="pl-s"><span class="pl-pds">'</span>Hello<span
                class="pl-pds">'</span></span>) <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-smi">s</span>.<span class="pl-en">endsWith</span>(<span class="pl-s"><span class="pl-pds">'</span>!<span
                class="pl-pds">'</span></span>) <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-smi">s</span>.<span class="pl-en">includes</span>(<span class="pl-s"><span class="pl-pds">'</span>o<span
                class="pl-pds">'</span></span>) <span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>这三个方法都支持第二个参数，表示开始搜索的位置。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s <span class="pl-k">=</span> <span
            class="pl-s"><span class="pl-pds">'</span>Hello world!<span class="pl-pds">'</span></span>;

<span class="pl-smi">s</span>.<span class="pl-en">startsWith</span>(<span class="pl-s"><span class="pl-pds">'</span>world<span
                class="pl-pds">'</span></span>, <span class="pl-c1">6</span>) <span class="pl-c"><span
                class="pl-c">//</span> true</span>
<span class="pl-smi">s</span>.<span class="pl-en">endsWith</span>(<span class="pl-s"><span
                class="pl-pds">'</span>Hello<span class="pl-pds">'</span></span>, <span class="pl-c1">5</span>) <span
                class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-smi">s</span>.<span class="pl-en">includes</span>(<span class="pl-s"><span
                class="pl-pds">'</span>Hello<span class="pl-pds">'</span></span>, <span class="pl-c1">6</span>) <span
                class="pl-c"><span class="pl-c">//</span> false</span></pre>
    </div>
    <p>上面代码表示，使用第二个参数<code>n</code>时，<code>endsWith</code>的行为与其他两个方法有所不同。它针对前<code>n</code>个字符，而其他两个方法针对从第<code>n</code>个位置直到字符串结束。
    </p>
    <h2><a href="#repeat"  class="anchor" id="user-content-repeat">

    </a>repeat()
    </h2>
    <p><code>repeat</code>方法返回一个新字符串，表示将原字符串重复<code>n</code>次。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>x<span
            class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span class="pl-c1">3</span>) <span
            class="pl-c"><span class="pl-c">//</span> "xxx"</span>
<span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>.<span
                class="pl-en">repeat</span>(<span class="pl-c1">2</span>) <span class="pl-c"><span
                class="pl-c">//</span> "hellohello"</span>
<span class="pl-s"><span class="pl-pds">'</span>na<span class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span
                class="pl-c1">0</span>) <span class="pl-c"><span class="pl-c">//</span> ""</span></pre>
    </div>
    <p>参数如果是小数，会被取整。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-s"><span class="pl-pds">'</span>na<span class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span
                class="pl-c1">2.9</span>) <span class="pl-c"><span class="pl-c">//</span> "nana"</span></pre>
    </div>
    <p>如果<code>repeat</code>的参数是负数或者<code>Infinity</code>，会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>na<span
            class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span class="pl-c1">Infinity</span>)
<span class="pl-c"><span class="pl-c">//</span> RangeError</span>
<span class="pl-s"><span class="pl-pds">'</span>na<span class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span
                class="pl-k">-</span><span class="pl-c1">1</span>)
<span class="pl-c"><span class="pl-c">//</span> RangeError</span></pre>
    </div>
    <p>但是，如果参数是 0 到-1 之间的小数，则等同于 0，这是因为会先进行取整运算。0 到-1 之间的小数，取整以后等于<code>-0</code>，<code>repeat</code>视同为 0。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-s"><span class="pl-pds">'</span>na<span class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span
                class="pl-k">-</span><span class="pl-c1">0.9</span>) <span class="pl-c"><span class="pl-c">//</span> ""</span></pre>
    </div>
    <p>参数<code>NaN</code>等同于 0。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-s"><span class="pl-pds">'</span>na<span class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span
                class="pl-c1">NaN</span>) <span class="pl-c"><span class="pl-c">//</span> ""</span></pre>
    </div>
    <p>如果<code>repeat</code>的参数是字符串，则会先转换成数字。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>na<span
            class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span class="pl-s"><span
            class="pl-pds">'</span>na<span class="pl-pds">'</span></span>) <span class="pl-c"><span
            class="pl-c">//</span> ""</span>
<span class="pl-s"><span class="pl-pds">'</span>na<span class="pl-pds">'</span></span>.<span class="pl-en">repeat</span>(<span
                class="pl-s"><span class="pl-pds">'</span>3<span class="pl-pds">'</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> "nanana"</span></pre>
    </div>
    <h2><a href="#padstartpadend"  class="anchor" id="user-content-padstartpadend">

    </a>padStart()，padEnd()
    </h2>
    <p>ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度，会在头部或尾部补全。<code>padStart()</code>用于头部补全，<code>padEnd()</code>用于尾部补全。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>x<span
            class="pl-pds">'</span></span>.<span class="pl-en">padStart</span>(<span class="pl-c1">5</span>, <span
            class="pl-s"><span class="pl-pds">'</span>ab<span class="pl-pds">'</span></span>) <span class="pl-c"><span
            class="pl-c">//</span> 'ababx'</span>
<span class="pl-s"><span class="pl-pds">'</span>x<span class="pl-pds">'</span></span>.<span
                class="pl-en">padStart</span>(<span class="pl-c1">4</span>, <span class="pl-s"><span
                class="pl-pds">'</span>ab<span class="pl-pds">'</span></span>) <span class="pl-c"><span
                class="pl-c">//</span> 'abax'</span>

<span class="pl-s"><span class="pl-pds">'</span>x<span class="pl-pds">'</span></span>.<span class="pl-en">padEnd</span>(<span
                class="pl-c1">5</span>, <span class="pl-s"><span class="pl-pds">'</span>ab<span class="pl-pds">'</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> 'xabab'</span>
<span class="pl-s"><span class="pl-pds">'</span>x<span class="pl-pds">'</span></span>.<span class="pl-en">padEnd</span>(<span
                class="pl-c1">4</span>, <span class="pl-s"><span class="pl-pds">'</span>ab<span class="pl-pds">'</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> 'xaba'</span></pre>
    </div>
    <p>上面代码中，<code>padStart</code>和<code>padEnd</code>一共接受两个参数，第一个参数用来指定字符串的最小长度，第二个参数是用来补全的字符串。</p>
    <p>如果原字符串的长度，等于或大于指定的最小长度，则返回原字符串。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>xxx<span
            class="pl-pds">'</span></span>.<span class="pl-en">padStart</span>(<span class="pl-c1">2</span>, <span
            class="pl-s"><span class="pl-pds">'</span>ab<span class="pl-pds">'</span></span>) <span class="pl-c"><span
            class="pl-c">//</span> 'xxx'</span>
<span class="pl-s"><span class="pl-pds">'</span>xxx<span class="pl-pds">'</span></span>.<span
                class="pl-en">padEnd</span>(<span class="pl-c1">2</span>, <span class="pl-s"><span
                class="pl-pds">'</span>ab<span class="pl-pds">'</span></span>) <span class="pl-c"><span
                class="pl-c">//</span> 'xxx'</span></pre>
    </div>
    <p>如果用来补全的字符串与原字符串，两者的长度之和超过了指定的最小长度，则会截去超出位数的补全字符串。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>abc<span
            class="pl-pds">'</span></span>.<span class="pl-en">padStart</span>(<span class="pl-c1">10</span>, <span
            class="pl-s"><span class="pl-pds">'</span>0123456789<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> '0123456abc'</span></pre>
    </div>
    <p>如果省略第二个参数，默认使用空格补全长度。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>x<span
            class="pl-pds">'</span></span>.<span class="pl-en">padStart</span>(<span class="pl-c1">4</span>) <span
            class="pl-c"><span class="pl-c">//</span> '   x'</span>
<span class="pl-s"><span class="pl-pds">'</span>x<span class="pl-pds">'</span></span>.<span class="pl-en">padEnd</span>(<span
                class="pl-c1">4</span>) <span class="pl-c"><span class="pl-c">//</span> 'x   '</span></pre>
    </div>
    <p><code>padStart</code>的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>1<span
            class="pl-pds">'</span></span>.<span class="pl-en">padStart</span>(<span class="pl-c1">10</span>, <span
            class="pl-s"><span class="pl-pds">'</span>0<span class="pl-pds">'</span></span>) <span class="pl-c"><span
            class="pl-c">//</span> "0000000001"</span>
<span class="pl-s"><span class="pl-pds">'</span>12<span class="pl-pds">'</span></span>.<span
                class="pl-en">padStart</span>(<span class="pl-c1">10</span>, <span class="pl-s"><span
                class="pl-pds">'</span>0<span class="pl-pds">'</span></span>) <span class="pl-c"><span
                class="pl-c">//</span> "0000000012"</span>
<span class="pl-s"><span class="pl-pds">'</span>123456<span class="pl-pds">'</span></span>.<span
                class="pl-en">padStart</span>(<span class="pl-c1">10</span>, <span class="pl-s"><span
                class="pl-pds">'</span>0<span class="pl-pds">'</span></span>) <span class="pl-c"><span
                class="pl-c">//</span> "0000123456"</span></pre>
    </div>
    <p>另一个用途是提示字符串格式。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">'</span>12<span
            class="pl-pds">'</span></span>.<span class="pl-en">padStart</span>(<span class="pl-c1">10</span>, <span
            class="pl-s"><span class="pl-pds">'</span>YYYY-MM-DD<span class="pl-pds">'</span></span>) <span
            class="pl-c"><span class="pl-c">//</span> "YYYY-MM-12"</span>
<span class="pl-s"><span class="pl-pds">'</span>09-12<span class="pl-pds">'</span></span>.<span
                class="pl-en">padStart</span>(<span class="pl-c1">10</span>, <span class="pl-s"><span
                class="pl-pds">'</span>YYYY-MM-DD<span class="pl-pds">'</span></span>) <span class="pl-c"><span
                class="pl-c">//</span> "YYYY-09-12"</span></pre>
    </div>
    <h2><a href="#模板字符串"  class="anchor" id="user-content-模板字符串">

    </a>模板字符串
    </h2>
    <p>传统的 JavaScript 语言，输出模板通常是这样写的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">$</span>(<span class="pl-s"><span
            class="pl-pds">'</span>#result<span class="pl-pds">'</span></span>).<span class="pl-c1">append</span>(
  <span class="pl-s"><span class="pl-pds">'</span>There are &lt;b&gt;<span class="pl-pds">'</span></span> <span
                class="pl-k">+</span> <span class="pl-smi">basket</span>.<span class="pl-smi">count</span> <span
                class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span>&lt;/b&gt; <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span>
  <span class="pl-s"><span class="pl-pds">'</span>items in your basket, <span class="pl-pds">'</span></span> <span
                class="pl-k">+</span>
  <span class="pl-s"><span class="pl-pds">'</span>&lt;em&gt;<span class="pl-pds">'</span></span> <span
                class="pl-k">+</span> <span class="pl-smi">basket</span>.<span class="pl-smi">onSale</span> <span
                class="pl-k">+</span>
  <span class="pl-s"><span class="pl-pds">'</span>&lt;/em&gt; are on sale!<span class="pl-pds">'</span></span>
);</pre>
    </div>
    <p>上面这种写法相当繁琐不方便，ES6 引入了模板字符串解决这个问题。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">$</span>(<span class="pl-s"><span
            class="pl-pds">'</span>#result<span class="pl-pds">'</span></span>).<span class="pl-c1">append</span>(<span
            class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s">  There are &lt;b&gt;<span class="pl-s1"><span class="pl-pse">${</span><span
        class="pl-smi">basket</span>.<span class="pl-smi">count</span><span class="pl-pse">}</span></span>&lt;/b&gt; items</span>
<span class="pl-s">   in your basket, &lt;em&gt;<span class="pl-s1"><span class="pl-pse">${</span><span class="pl-smi">basket</span>.<span
        class="pl-smi">onSale</span><span class="pl-pse">}</span></span>&lt;/em&gt;</span>
<span class="pl-s">  are on sale!</span>
<span class="pl-s"><span class="pl-pds">`</span></span>);</pre>
    </div>
    <p>模板字符串（template string）是增强版的字符串，用反引号（`）标识。它可以当作普通字符串使用，也可以用来定义多行字符串，或者在字符串中嵌入变量。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 普通字符串</span>
<span class="pl-s"><span class="pl-pds">`</span>In JavaScript '<span class="pl-cce">\n</span>' is a line-feed.<span
        class="pl-pds">`</span></span>

<span class="pl-c"><span class="pl-c">//</span> 多行字符串</span>
<span class="pl-s"><span class="pl-pds">`</span>In JavaScript this is</span>
<span class="pl-s"> not legal.<span class="pl-pds">`</span></span>

<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span>string text line 1</span>
<span class="pl-s">string text line 2<span class="pl-pds">`</span></span>);

<span class="pl-c"><span class="pl-c">//</span> 字符串中嵌入变量</span>
<span class="pl-k">let</span> name <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>Bob<span
                class="pl-pds">"</span></span>, time <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">"</span>today<span class="pl-pds">"</span></span>;
<span class="pl-s"><span class="pl-pds">`</span>Hello <span class="pl-s1"><span class="pl-pse">${</span>name<span
        class="pl-pse">}</span></span>, how are you <span class="pl-s1"><span class="pl-pse">${</span>time<span
        class="pl-pse">}</span></span>?<span class="pl-pds">`</span></span></pre>
    </div>
    <p>上面代码中的模板字符串，都是用反引号表示。如果在模板字符串中需要使用反引号，则前面要用反斜杠转义。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">let</span> greeting <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">`</span><span
                class="pl-cce">\`</span>Yo<span class="pl-cce">\`</span> World!<span
                class="pl-pds">`</span></span>;</pre>
    </div>
    <p>如果使用模板字符串表示多行字符串，所有的空格和缩进都会被保留在输出之中。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">$</span>(<span class="pl-s"><span
            class="pl-pds">'</span>#list<span class="pl-pds">'</span></span>).<span class="pl-en">html</span>(<span
            class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s">&lt;ul&gt;</span>
<span class="pl-s">  &lt;li&gt;first&lt;/li&gt;</span>
<span class="pl-s">  &lt;li&gt;second&lt;/li&gt;</span>
<span class="pl-s">&lt;/ul&gt;</span>
<span class="pl-s"><span class="pl-pds">`</span></span>);</pre>
    </div>
    <p>上面代码中，所有模板字符串的空格和换行，都是被保留的，比如<code>&lt;ul&gt;</code>标签前面会有一个换行。如果你不想要这个换行，可以使用<code>trim</code>方法消除它。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">$</span>(<span class="pl-s"><span
            class="pl-pds">'</span>#list<span class="pl-pds">'</span></span>).<span class="pl-en">html</span>(<span
            class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s">&lt;ul&gt;</span>
<span class="pl-s">  &lt;li&gt;first&lt;/li&gt;</span>
<span class="pl-s">  &lt;li&gt;second&lt;/li&gt;</span>
<span class="pl-s">&lt;/ul&gt;</span>
<span class="pl-s"><span class="pl-pds">`</span></span>.<span class="pl-en">trim</span>());</pre>
    </div>
    <p>模板字符串中嵌入变量，需要将变量名写在<code>${}</code>之中。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">authorize</span>(<span class="pl-smi">user</span>, <span class="pl-smi">action</span>) {
  <span class="pl-k">if</span> (<span class="pl-k">!</span><span class="pl-smi">user</span>.<span class="pl-en">hasPrivilege</span>(action)) {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(
      <span class="pl-c"><span class="pl-c">//</span> 传统写法为</span>
      <span class="pl-c"><span class="pl-c">//</span> 'User '</span>
      <span class="pl-c"><span class="pl-c">//</span> + user.name</span>
      <span class="pl-c"><span class="pl-c">//</span> + ' is not authorized to do '</span>
      <span class="pl-c"><span class="pl-c">//</span> + action</span>
      <span class="pl-c"><span class="pl-c">//</span> + '.'</span>
      <span class="pl-s"><span class="pl-pds">`</span>User <span class="pl-s1"><span class="pl-pse">${</span><span
              class="pl-smi">user</span>.<span class="pl-c1">name</span><span class="pl-pse">}</span></span> is not authorized to do <span
              class="pl-s1"><span class="pl-pse">${</span>action<span class="pl-pse">}</span></span>.<span
              class="pl-pds">`</span></span>);
  }
}</pre>
    </div>
    <p>大括号内部可以放入任意的 JavaScript 表达式，可以进行运算，以及引用对象属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> x <span class="pl-k">=</span> <span
            class="pl-c1">1</span>;
<span class="pl-k">let</span> y <span class="pl-k">=</span> <span class="pl-c1">2</span>;

<span class="pl-s"><span class="pl-pds">`</span><span class="pl-s1"><span class="pl-pse">${</span>x<span class="pl-pse">}</span></span> + <span
        class="pl-s1"><span class="pl-pse">${</span>y<span class="pl-pse">}</span></span> = <span class="pl-s1"><span
        class="pl-pse">${</span>x <span class="pl-k">+</span> y<span class="pl-pse">}</span></span><span class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> "1 + 2 = 3"</span>

<span class="pl-s"><span class="pl-pds">`</span><span class="pl-s1"><span class="pl-pse">${</span>x<span class="pl-pse">}</span></span> + <span
        class="pl-s1"><span class="pl-pse">${</span>y <span class="pl-k">*</span> <span class="pl-c1">2</span><span
        class="pl-pse">}</span></span> = <span class="pl-s1"><span class="pl-pse">${</span>x <span class="pl-k">+</span> y <span
        class="pl-k">*</span> <span class="pl-c1">2</span><span class="pl-pse">}</span></span><span
        class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> "1 + 4 = 5"</span>

<span class="pl-k">let</span> obj <span class="pl-k">=</span> {x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span
                class="pl-k">:</span> <span class="pl-c1">2</span>};
<span class="pl-s"><span class="pl-pds">`</span><span class="pl-s1"><span class="pl-pse">${</span><span class="pl-smi">obj</span>.<span
        class="pl-c1">x</span> <span class="pl-k">+</span> <span class="pl-smi">obj</span>.<span
        class="pl-c1">y</span><span class="pl-pse">}</span></span><span class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> "3"</span></pre>
    </div>
    <p>模板字符串之中还能调用函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">fn</span>() {
  <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>Hello World<span
                class="pl-pds">"</span></span>;
}

<span class="pl-s"><span class="pl-pds">`</span>foo <span class="pl-s1"><span class="pl-pse">${</span><span
        class="pl-en">fn</span>()<span class="pl-pse">}</span></span> bar<span class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> foo Hello World bar</span></pre>
    </div>
    <p>如果大括号中的值不是字符串，将按照一般的规则转为字符串。比如，大括号中是一个对象，将默认调用对象的<code>toString</code>方法。</p>
    <p>如果模板字符串中的变量没有声明，将报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 变量place没有声明</span>
<span class="pl-k">let</span> msg <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">`</span>Hello, <span class="pl-s1"><span class="pl-pse">${</span>place<span
                class="pl-pse">}</span></span><span class="pl-pds">`</span></span>;
<span class="pl-c"><span class="pl-c">//</span> 报错</span></pre>
    </div>
    <p>由于模板字符串的大括号内部，就是执行 JavaScript 代码，因此如果大括号内部是一个字符串，将会原样输出。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-s"><span class="pl-pds">`</span>Hello <span
            class="pl-s1"><span class="pl-pse">${</span><span class="pl-s"><span class="pl-pds">'</span>World<span
            class="pl-pds">'</span></span><span class="pl-pse">}</span></span><span class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> "Hello World"</span></pre>
    </div>
    <p>模板字符串甚至还能嵌套。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">tmpl</span> <span class="pl-k">=</span> <span class="pl-smi">addrs</span> <span class="pl-k">=&gt;</span> <span
            class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s">  &lt;table&gt;</span>
<span class="pl-s">  <span class="pl-s1"><span class="pl-pse">${</span><span class="pl-smi">addrs</span>.<span
        class="pl-en">map</span>(<span class="pl-smi">addr</span> <span class="pl-k">=&gt;</span> <span
        class="pl-s"><span class="pl-pds">`</span></span></span></span>
<span class="pl-s"><span class="pl-s1"><span class="pl-s">    &lt;tr&gt;&lt;td&gt;<span class="pl-s1"><span
        class="pl-pse">${</span><span class="pl-smi">addr</span>.<span class="pl-smi">first</span><span
        class="pl-pse">}</span></span>&lt;/td&gt;&lt;/tr&gt;</span></span></span>
<span class="pl-s"><span class="pl-s1"><span class="pl-s">    &lt;tr&gt;&lt;td&gt;<span class="pl-s1"><span
        class="pl-pse">${</span><span class="pl-smi">addr</span>.<span class="pl-smi">last</span><span
        class="pl-pse">}</span></span>&lt;/td&gt;&lt;/tr&gt;</span></span></span>
<span class="pl-s"><span class="pl-s1"><span class="pl-s">  <span class="pl-pds">`</span></span>).<span class="pl-c1">join</span>(<span
        class="pl-s"><span class="pl-pds">'</span><span class="pl-pds">'</span></span>)<span
        class="pl-pse">}</span></span></span>
<span class="pl-s">  &lt;/table&gt;</span>
<span class="pl-s"><span class="pl-pds">`</span></span>;</pre>
    </div>
    <p>上面代码中，模板字符串的变量之中，又嵌入了另一个模板字符串，使用方法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">data</span> <span class="pl-k">=</span> [
    { first<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>&lt;Jane&gt;<span class="pl-pds">'</span></span>, last<span
                class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Bond<span class="pl-pds">'</span></span> },
    { first<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Lars<span class="pl-pds">'</span></span>, last<span
                class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>&lt;Croft&gt;<span class="pl-pds">'</span></span> },
];

<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-en">tmpl</span>(data));
<span class="pl-c"><span class="pl-c">//</span> &lt;table&gt;</span>
<span class="pl-c"><span class="pl-c">//</span></span>
<span class="pl-c"><span class="pl-c">//</span>   &lt;tr&gt;&lt;td&gt;&lt;Jane&gt;&lt;/td&gt;&lt;/tr&gt;</span>
<span class="pl-c"><span class="pl-c">//</span>   &lt;tr&gt;&lt;td&gt;Bond&lt;/td&gt;&lt;/tr&gt;</span>
<span class="pl-c"><span class="pl-c">//</span></span>
<span class="pl-c"><span class="pl-c">//</span>   &lt;tr&gt;&lt;td&gt;Lars&lt;/td&gt;&lt;/tr&gt;</span>
<span class="pl-c"><span class="pl-c">//</span>   &lt;tr&gt;&lt;td&gt;&lt;Croft&gt;&lt;/td&gt;&lt;/tr&gt;</span>
<span class="pl-c"><span class="pl-c">//</span></span>
<span class="pl-c"><span class="pl-c">//</span> &lt;/table&gt;</span></pre>
    </div>
    <p>如果需要引用模板字符串本身，在需要时执行，可以像下面这样写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 写法一</span>
<span class="pl-k">let</span> str <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>return <span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span
                class="pl-s"><span class="pl-pds">'</span>`Hello ${name}!`<span class="pl-pds">'</span></span>;
<span class="pl-k">let</span> func <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Function</span>(<span
                class="pl-s"><span class="pl-pds">'</span>name<span class="pl-pds">'</span></span>, str);
<span class="pl-en">func</span>(<span class="pl-s"><span class="pl-pds">'</span>Jack<span class="pl-pds">'</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> "Hello Jack!"</span>

<span class="pl-c"><span class="pl-c">//</span> 写法二</span>
<span class="pl-k">let</span> str <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>(name) =&gt; `Hello ${name}!`<span
                class="pl-pds">'</span></span>;
<span class="pl-k">let</span> func <span class="pl-k">=</span> <span class="pl-smi">eval</span>.<span
                class="pl-c1">call</span>(<span class="pl-c1">null</span>, str);
<span class="pl-en">func</span>(<span class="pl-s"><span class="pl-pds">'</span>Jack<span class="pl-pds">'</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> "Hello Jack!"</span></pre>
    </div>
    <h2><a href="#实例模板编译"  class="anchor" id="user-content-实例模板编译">

    </a>实例：模板编译
    </h2>
    <p>下面，我们来看一个通过模板字符串，生成正式模板的实例。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> template <span
            class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s">&lt;ul&gt;</span>
<span class="pl-s">  &lt;% for(let i=0; i &lt; data.supplies.length; i++) { %&gt;</span>
<span class="pl-s">    &lt;li&gt;&lt;%= data.supplies[i] %&gt;&lt;/li&gt;</span>
<span class="pl-s">  &lt;% } %&gt;</span>
<span class="pl-s">&lt;/ul&gt;</span>
<span class="pl-s"><span class="pl-pds">`</span></span>;</pre>
    </div>
    <p>上面代码在模板字符串之中，放置了一个常规模板。该模板使用<code>&lt;%...%&gt;</code>放置 JavaScript 代码，使用<code>&lt;%= ... %&gt;</code>输出
        JavaScript 表达式。</p>
    <p>怎么编译这个模板字符串呢？</p>
    <p>一种思路是将其转换为 JavaScript 表达式字符串。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">echo</span>(<span class="pl-s"><span
            class="pl-pds">'</span>&lt;ul&gt;<span class="pl-pds">'</span></span>);
<span class="pl-k">for</span>(<span class="pl-k">let</span> i<span class="pl-k">=</span><span class="pl-c1">0</span>; i <span
                class="pl-k">&lt;</span> <span class="pl-smi">data</span>.<span class="pl-smi">supplies</span>.<span
                class="pl-c1">length</span>; i<span class="pl-k">++</span>) {
  <span class="pl-en">echo</span>(<span class="pl-s"><span class="pl-pds">'</span>&lt;li&gt;<span
                class="pl-pds">'</span></span>);
  <span class="pl-en">echo</span>(<span class="pl-smi">data</span>.<span class="pl-smi">supplies</span>[i]);
  <span class="pl-en">echo</span>(<span class="pl-s"><span class="pl-pds">'</span>&lt;/li&gt;<span
                class="pl-pds">'</span></span>);
};
<span class="pl-en">echo</span>(<span class="pl-s"><span class="pl-pds">'</span>&lt;/ul&gt;<span class="pl-pds">'</span></span>);</pre>
    </div>
    <p>这个转换使用正则表达式就行了。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> evalExpr <span
            class="pl-k">=</span><span class="pl-sr"> <span class="pl-pds">/</span>&lt;%=(<span
            class="pl-c1">.</span><span class="pl-k">+?</span>)%&gt;<span class="pl-pds">/</span>g</span>;
<span class="pl-k">let</span> expr <span class="pl-k">=</span><span class="pl-sr"> <span
                class="pl-pds">/</span>&lt;%(<span class="pl-c1">[<span class="pl-c1">\s\S</span>]</span><span
                class="pl-k">+?</span>)%&gt;<span class="pl-pds">/</span>g</span>;

template <span class="pl-k">=</span> template
  .<span class="pl-c1">replace</span>(evalExpr, <span class="pl-s"><span class="pl-pds">'</span>`); <span
                class="pl-cce">\n</span>  echo( $1 ); <span class="pl-cce">\n</span>  echo(`<span
                class="pl-pds">'</span></span>)
  .<span class="pl-c1">replace</span>(expr, <span class="pl-s"><span class="pl-pds">'</span>`); <span
                class="pl-cce">\n</span> $1 <span class="pl-cce">\n</span>  echo(`<span class="pl-pds">'</span></span>);

template <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>echo(`<span class="pl-pds">'</span></span> <span
                class="pl-k">+</span> template <span class="pl-k">+</span> <span class="pl-s"><span
                class="pl-pds">'</span>`);<span class="pl-pds">'</span></span>;</pre>
    </div>
    <p>然后，将<code>template</code>封装在一个函数里面返回，就可以了。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> script <span class="pl-k">=</span>
<span class="pl-s"><span class="pl-pds">`</span>(function parse(data){</span>
<span class="pl-s">  let output = "";</span>
<span class="pl-s"></span>
<span class="pl-s">  function echo(html){</span>
<span class="pl-s">    output += html;</span>
<span class="pl-s">  }</span>
<span class="pl-s"></span>
<span class="pl-s">  <span class="pl-s1"><span class="pl-pse">${</span> template <span
        class="pl-pse">}</span></span></span>
<span class="pl-s"></span>
<span class="pl-s">  return output;</span>
<span class="pl-s">})<span class="pl-pds">`</span></span>;

<span class="pl-k">return</span> script;</pre>
    </div>
    <p>将上面的内容拼装成一个模板编译函数<code>compile</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">compile</span>(<span class="pl-smi">template</span>){
  <span class="pl-k">const</span> <span class="pl-c1">evalExpr</span> <span class="pl-k">=</span><span
                class="pl-sr"> <span class="pl-pds">/</span>&lt;%=(<span class="pl-c1">.</span><span
                class="pl-k">+?</span>)%&gt;<span class="pl-pds">/</span>g</span>;
  <span class="pl-k">const</span> <span class="pl-c1">expr</span> <span class="pl-k">=</span><span class="pl-sr"> <span
                class="pl-pds">/</span>&lt;%(<span class="pl-c1">[<span class="pl-c1">\s\S</span>]</span><span
                class="pl-k">+?</span>)%&gt;<span class="pl-pds">/</span>g</span>;

  template <span class="pl-k">=</span> template
    .<span class="pl-c1">replace</span>(evalExpr, <span class="pl-s"><span class="pl-pds">'</span>`); <span
                class="pl-cce">\n</span>  echo( $1 ); <span class="pl-cce">\n</span>  echo(`<span
                class="pl-pds">'</span></span>)
    .<span class="pl-c1">replace</span>(expr, <span class="pl-s"><span class="pl-pds">'</span>`); <span class="pl-cce">\n</span> $1 <span
                class="pl-cce">\n</span>  echo(`<span class="pl-pds">'</span></span>);

  template <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>echo(`<span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> template <span class="pl-k">+</span> <span
                class="pl-s"><span class="pl-pds">'</span>`);<span class="pl-pds">'</span></span>;

  <span class="pl-k">let</span> script <span class="pl-k">=</span>
  <span class="pl-s"><span class="pl-pds">`</span>(function parse(data){</span>
<span class="pl-s">    let output = "";</span>
<span class="pl-s"></span>
<span class="pl-s">    function echo(html){</span>
<span class="pl-s">      output += html;</span>
<span class="pl-s">    }</span>
<span class="pl-s"></span>
<span class="pl-s">    <span class="pl-s1"><span class="pl-pse">${</span> template <span class="pl-pse">}</span></span></span>
<span class="pl-s"></span>
<span class="pl-s">    return output;</span>
<span class="pl-s">  })<span class="pl-pds">`</span></span>;

  <span class="pl-k">return</span> script;
}</pre>
    </div>
    <p><code>compile</code>函数的用法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> parse <span
            class="pl-k">=</span> <span class="pl-c1">eval</span>(<span class="pl-en">compile</span>(template));
<span class="pl-smi">div</span>.<span class="pl-smi">innerHTML</span> <span class="pl-k">=</span> <span class="pl-en">parse</span>({ supplies<span
                class="pl-k">:</span> [ <span class="pl-s"><span class="pl-pds">"</span>broom<span
                class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>mop<span class="pl-pds">"</span></span>, <span
                class="pl-s"><span class="pl-pds">"</span>cleaner<span class="pl-pds">"</span></span> ] });
<span class="pl-c"><span class="pl-c">//</span>   &lt;ul&gt;</span>
<span class="pl-c"><span class="pl-c">//</span>     &lt;li&gt;broom&lt;/li&gt;</span>
<span class="pl-c"><span class="pl-c">//</span>     &lt;li&gt;mop&lt;/li&gt;</span>
<span class="pl-c"><span class="pl-c">//</span>     &lt;li&gt;cleaner&lt;/li&gt;</span>
<span class="pl-c"><span class="pl-c">//</span>   &lt;/ul&gt;</span></pre>
    </div>
    <h2><a href="#标签模板"  class="anchor" id="user-content-标签模板">

    </a>标签模板
    </h2>
    <p>模板字符串的功能，不仅仅是上面这些。它可以紧跟在一个函数名后面，该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能（tagged template）。</p>
    <div class="highlight highlight-source-js"><pre>alert<span class="pl-s"><span class="pl-pds">`</span>123<span
            class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-en">alert</span>(<span class="pl-c1">123</span>)</pre>
    </div>
    <p>标签模板其实不是模板，而是函数调用的一种特殊形式。“标签”指的就是函数，紧跟在后面的模板字符串就是它的参数。</p>
    <p>但是，如果模板字符里面有变量，就不是简单的调用了，而是会将模板字符串先处理成多个参数，再调用函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> a <span class="pl-k">=</span> <span
            class="pl-c1">5</span>;
<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-c1">10</span>;

tag<span class="pl-s"><span class="pl-pds">`</span>Hello <span class="pl-s1"><span class="pl-pse">${</span> a <span
                class="pl-k">+</span> b <span class="pl-pse">}</span></span> world <span class="pl-s1"><span
                class="pl-pse">${</span> a <span class="pl-k">*</span> b <span class="pl-pse">}</span></span><span
                class="pl-pds">`</span></span>;
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-en">tag</span>([<span class="pl-s"><span class="pl-pds">'</span>Hello <span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span> world <span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span><span
                class="pl-pds">'</span></span>], <span class="pl-c1">15</span>, <span class="pl-c1">50</span>);</pre>
    </div>
    <p>上面代码中，模板字符串前面有一个标识名<code>tag</code>，它是一个函数。整个表达式的返回值，就是<code>tag</code>函数处理模板字符串后的返回值。</p>
    <p>函数<code>tag</code>依次会接收到多个参数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">tag</span>(<span class="pl-smi">stringArr</span>, <span class="pl-smi">value1</span>, <span
            class="pl-smi">value2</span>){
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>

<span class="pl-k">function</span> <span class="pl-en">tag</span>(<span class="pl-smi">stringArr</span>, <span
                class="pl-k">...</span><span class="pl-v">values</span>){
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}</pre>
    </div>
    <p><code>tag</code>函数的第一个参数是一个数组，该数组的成员是模板字符串中那些没有变量替换的部分，也就是说，变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间，以此类推。</p>
    <p><code>tag</code>函数的其他参数，都是模板字符串各个变量被替换后的值。由于本例中，模板字符串含有两个变量，因此<code>tag</code>会接受到<code>value1</code>和<code>value2</code>两个参数。
    </p>
    <p><code>tag</code>函数所有参数的实际值如下。</p>
    <ul>
        <li>第一个参数：<code>['Hello ', ' world ', '']</code></li>
        <li>第二个参数: 15</li>
        <li>第三个参数：50</li>
    </ul>
    <p>也就是说，<code>tag</code>函数实际上以下面的形式调用。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-en">tag</span>([<span class="pl-s"><span class="pl-pds">'</span>Hello <span class="pl-pds">'</span></span>, <span
                class="pl-s"><span class="pl-pds">'</span> world <span class="pl-pds">'</span></span>, <span
                class="pl-s"><span class="pl-pds">'</span><span class="pl-pds">'</span></span>], <span
                class="pl-c1">15</span>, <span class="pl-c1">50</span>)</pre>
    </div>
    <p>我们可以按照需要编写<code>tag</code>函数的代码。下面是<code>tag</code>函数的一种写法，以及运行结果。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> a <span class="pl-k">=</span> <span
            class="pl-c1">5</span>;
<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-c1">10</span>;

<span class="pl-k">function</span> <span class="pl-en">tag</span>(<span class="pl-smi">s</span>, <span
                class="pl-smi">v1</span>, <span class="pl-smi">v2</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(s[<span class="pl-c1">0</span>]);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(s[<span class="pl-c1">1</span>]);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(s[<span class="pl-c1">2</span>]);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(v1);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(v2);

  <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>OK<span
                class="pl-pds">"</span></span>;
}

tag<span class="pl-s"><span class="pl-pds">`</span>Hello <span class="pl-s1"><span class="pl-pse">${</span> a <span
                class="pl-k">+</span> b <span class="pl-pse">}</span></span> world <span class="pl-s1"><span
                class="pl-pse">${</span> a <span class="pl-k">*</span> b<span class="pl-pse">}</span></span><span
                class="pl-pds">`</span></span>;
<span class="pl-c"><span class="pl-c">//</span> "Hello "</span>
<span class="pl-c"><span class="pl-c">//</span> " world "</span>
<span class="pl-c"><span class="pl-c">//</span> ""</span>
<span class="pl-c"><span class="pl-c">//</span> 15</span>
<span class="pl-c"><span class="pl-c">//</span> 50</span>
<span class="pl-c"><span class="pl-c">//</span> "OK"</span></pre>
    </div>
    <p>下面是一个更复杂的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> total <span
            class="pl-k">=</span> <span class="pl-c1">30</span>;
<span class="pl-k">let</span> msg <span class="pl-k">=</span> passthru<span class="pl-s"><span class="pl-pds">`</span>The total is <span
                class="pl-s1"><span class="pl-pse">${</span>total<span class="pl-pse">}</span></span> (<span
                class="pl-s1"><span class="pl-pse">${</span>total<span class="pl-k">*</span><span
                class="pl-c1">1.05</span><span class="pl-pse">}</span></span> with tax)<span
                class="pl-pds">`</span></span>;

<span class="pl-k">function</span> <span class="pl-en">passthru</span>(<span class="pl-smi">literals</span>) {
  <span class="pl-k">let</span> result <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span><span
                class="pl-pds">'</span></span>;
  <span class="pl-k">let</span> i <span class="pl-k">=</span> <span class="pl-c1">0</span>;

  <span class="pl-k">while</span> (i <span class="pl-k">&lt;</span> <span class="pl-smi">literals</span>.<span
                class="pl-c1">length</span>) {
    result <span class="pl-k">+=</span> literals[i<span class="pl-k">++</span>];
    <span class="pl-k">if</span> (i <span class="pl-k">&lt;</span> <span class="pl-c1">arguments</span>.<span
                class="pl-c1">length</span>) {
      result <span class="pl-k">+=</span> <span class="pl-c1">arguments</span>[i];
    }
  }

  <span class="pl-k">return</span> result;
}

msg <span class="pl-c"><span class="pl-c">//</span> "The total is 30 (31.5 with tax)"</span></pre>
    </div>
    <p>上面这个例子展示了，如何将各个参数按照原来的位置拼合回去。</p>
    <p><code>passthru</code>函数采用 rest 参数的写法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">passthru</span>(<span class="pl-smi">literals</span>, <span class="pl-k">...</span><span
            class="pl-v">values</span>) {
  <span class="pl-k">let</span> output <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span><span
                class="pl-pds">"</span></span>;
  <span class="pl-k">let</span> index;
  <span class="pl-k">for</span> (index <span class="pl-k">=</span> <span class="pl-c1">0</span>; index <span
                class="pl-k">&lt;</span> <span class="pl-smi">values</span>.<span
                class="pl-c1">length</span>; index<span class="pl-k">++</span>) {
    output <span class="pl-k">+=</span> literals[index] <span class="pl-k">+</span> values[index];
  }

  output <span class="pl-k">+=</span> literals[index]
  <span class="pl-k">return</span> output;
}</pre>
    </div>
    <p>“标签模板”的一个重要应用，就是过滤 HTML 字符串，防止用户输入恶意内容。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> message <span class="pl-k">=</span>
  <span class="pl-s"><span class="pl-en">SaferHTML</span><span class="pl-pds">`</span>&lt;<span class="pl-ent">p</span>&gt;<span
          class="pl-s1"><span class="pl-pse">${</span>sender<span class="pl-pse">}</span></span> has sent you a message.&lt;/<span
          class="pl-ent">p</span>&gt;<span class="pl-pds">`</span></span>;

<span class="pl-k">function</span> <span class="pl-en">SaferHTML</span>(<span class="pl-smi">templateData</span>) {
  <span class="pl-k">let</span> s <span class="pl-k">=</span> templateData[<span class="pl-c1">0</span>];
  <span class="pl-k">for</span> (<span class="pl-k">let</span> i <span class="pl-k">=</span> <span
                class="pl-c1">1</span>; i <span class="pl-k">&lt;</span> <span class="pl-c1">arguments</span>.<span
                class="pl-c1">length</span>; i<span class="pl-k">++</span>) {
    <span class="pl-k">let</span> arg <span class="pl-k">=</span> <span class="pl-c1">String</span>(<span class="pl-c1">arguments</span>[i]);

    <span class="pl-c"><span class="pl-c">//</span> Escape special characters in the substitution.</span>
    s <span class="pl-k">+=</span> <span class="pl-smi">arg</span>.<span class="pl-c1">replace</span>(<span
                class="pl-sr"><span class="pl-pds">/</span>&amp;<span class="pl-pds">/</span>g</span>, <span
                class="pl-s"><span class="pl-pds">"</span>&amp;amp;<span class="pl-pds">"</span></span>)
            .<span class="pl-c1">replace</span>(<span class="pl-sr"><span class="pl-pds">/</span>&lt;<span
                class="pl-pds">/</span>g</span>, <span class="pl-s"><span class="pl-pds">"</span>&amp;lt;<span
                class="pl-pds">"</span></span>)
            .<span class="pl-c1">replace</span>(<span class="pl-sr"><span class="pl-pds">/</span>&gt;<span
                class="pl-pds">/</span>g</span>, <span class="pl-s"><span class="pl-pds">"</span>&amp;gt;<span
                class="pl-pds">"</span></span>);

    <span class="pl-c"><span class="pl-c">//</span> Don't escape special characters in the template.</span>
    s <span class="pl-k">+=</span> templateData[i];
  }
  <span class="pl-k">return</span> s;
}</pre>
    </div>
    <p>上面代码中，<code>sender</code>变量往往是用户提供的，经过<code>SaferHTML</code>函数处理，里面的特殊字符都会被转义。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> sender <span
            class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>&lt;script&gt;alert("abc")&lt;/script&gt;<span
            class="pl-pds">'</span></span>; <span class="pl-c"><span class="pl-c">//</span> 恶意代码</span>
<span class="pl-k">let</span> message <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-en">SaferHTML</span><span class="pl-pds">`</span>&lt;<span class="pl-ent">p</span>&gt;<span
                class="pl-s1"><span class="pl-pse">${</span>sender<span class="pl-pse">}</span></span> has sent you a message.&lt;/<span
                class="pl-ent">p</span>&gt;<span class="pl-pds">`</span></span>;

message
<span class="pl-c"><span class="pl-c">//</span> &lt;p&gt;&amp;lt;script&amp;gt;alert("abc")&amp;lt;/script&amp;gt; has sent you a message.&lt;/p&gt;</span></pre>
    </div>
    <p>标签模板的另一个应用，就是多语言转换（国际化处理）。</p>
    <div class="highlight highlight-source-js"><pre>i18n<span class="pl-s"><span class="pl-pds">`</span>Welcome to <span
            class="pl-s1"><span class="pl-pse">${</span>siteName<span class="pl-pse">}</span></span>, you are visitor number <span
            class="pl-s1"><span class="pl-pse">${</span>visitorNumber<span class="pl-pse">}</span></span>!<span
            class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> "欢迎访问xxx，您是第xxxx位访问者！"</span></pre>
    </div>
    <p>模板字符串本身并不能取代 Mustache 之类的模板库，因为没有条件判断和循环处理功能，但是通过标签函数，你可以自己添加这些功能。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> 下面的hashTemplate函数</span>
<span class="pl-c"><span class="pl-c">//</span> 是一个自定义的模板处理函数</span>
<span class="pl-k">let</span> libraryHtml <span class="pl-k">=</span> hashTemplate<span class="pl-s"><span
                class="pl-pds">`</span></span>
<span class="pl-s">  &lt;ul&gt;</span>
<span class="pl-s">    #for book in <span class="pl-s1"><span class="pl-pse">${</span>myBooks<span
        class="pl-pse">}</span></span></span>
<span class="pl-s">      &lt;li&gt;&lt;i&gt;#{book.title}&lt;/i&gt; by #{book.author}&lt;/li&gt;</span>
<span class="pl-s">    #end</span>
<span class="pl-s">  &lt;/ul&gt;</span>
<span class="pl-s"><span class="pl-pds">`</span></span>;</pre>
    </div>
    <p>除此之外，你甚至可以使用标签模板，在 JavaScript 语言之中嵌入其他语言。</p>
    <div class="highlight highlight-source-js"><pre>jsx<span class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s">  &lt;div&gt;</span>
<span class="pl-s">    &lt;input</span>
<span class="pl-s">      ref='input'</span>
<span class="pl-s">      onChange='<span class="pl-s1"><span class="pl-pse">${</span><span
        class="pl-c1">this</span>.<span class="pl-smi">handleChange</span><span class="pl-pse">}</span></span>'</span>
<span class="pl-s">      defaultValue='<span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">this</span>.<span
        class="pl-smi">state</span>.<span class="pl-c1">value</span><span class="pl-pse">}</span></span>' /&gt;</span>
<span class="pl-s">      <span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">this</span>.<span
        class="pl-smi">state</span>.<span class="pl-c1">value</span><span class="pl-pse">}</span></span></span>
<span class="pl-s">   &lt;/div&gt;</span>
<span class="pl-s"><span class="pl-pds">`</span></span></pre>
    </div>
    <p>上面的代码通过<code>jsx</code>函数，将一个 DOM 字符串转为 React 对象。你可以在 Github 找到<code>jsx</code>函数的<a
            href="https://gist.github.com/lygaret/a68220defa69174bdec5">具体实现</a>。</p>
    <p>下面则是一个假想的例子，通过<code>java</code>函数，在 JavaScript 代码之中运行 Java 代码。</p>
    <div class="highlight highlight-source-js"><pre>java<span class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s">class HelloWorldApp {</span>
<span class="pl-s">  public static void main(String[] args) {</span>
<span class="pl-s">    System.out.println(“Hello World!”); // Display the string.</span>
<span class="pl-s">  }</span>
<span class="pl-s">}</span>
<span class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-smi">HelloWorldApp</span>.<span class="pl-en">main</span>();</pre>
    </div>
    <p>模板处理函数的第一个参数（模板字符串数组），还有一个<code>raw</code>属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">console</span>.<span
            class="pl-smi">log</span><span class="pl-s"><span class="pl-pds">`</span>123<span
            class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> ["123", raw: Array[1]]</span></pre>
    </div>
    <p>上面代码中，<code>console.log</code>接受的参数，实际上是一个数组。该数组有一个<code>raw</code>属性，保存的是转义后的原字符串。</p>
    <p>请看下面的例子。</p>
    <div class="highlight highlight-source-js"><pre>tag<span class="pl-s"><span class="pl-pds">`</span>First line<span
            class="pl-cce">\n</span>Second line<span class="pl-pds">`</span></span>

<span class="pl-k">function</span> <span class="pl-en">tag</span>(<span class="pl-smi">strings</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">strings</span>.<span
                class="pl-smi">raw</span>[<span class="pl-c1">0</span>]);
  <span class="pl-c"><span class="pl-c">//</span> strings.raw[0] 为 "First line\\nSecond line"</span>
  <span class="pl-c"><span class="pl-c">//</span> 打印输出 "First line\nSecond line"</span>
}</pre>
    </div>
    <p>上面代码中，<code>tag</code>函数的第一个参数<code>strings</code>，有一个<code>raw</code>属性，也指向一个数组。该数组的成员与<code>strings</code>数组完全一致。比如，<code>strings</code>数组是<code>["First
        line\nSecond line"]</code>，那么<code>strings.raw</code>数组就是<code>["First line\\nSecond line"]</code>。两者唯一的区别，就是字符串里面的斜杠都被转义了。比如，strings.raw
        数组会将<code>\n</code>视为<code>\\</code>和<code>n</code>两个字符，而不是换行符。这是为了方便取得转义之前的原始模板而设计的。</p>
    <h2><a href="#stringraw"  class="anchor" id="user-content-stringraw">

    </a>String.raw()
    </h2>
    <p>ES6 还为原生的 String 对象，提供了一个<code>raw</code>方法。</p>
    <p><code>String.raw</code>方法，往往用来充当模板字符串的处理函数，返回一个斜杠都被转义（即斜杠前面再加一个斜杠）的字符串，对应于替换变量后的模板字符串。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-smi">raw</span><span class="pl-s"><span class="pl-pds">`</span>Hi<span
            class="pl-cce">\n</span><span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">2</span><span
            class="pl-k">+</span><span class="pl-c1">3</span><span class="pl-pse">}</span></span>!<span
            class="pl-pds">`</span></span>;
<span class="pl-c"><span class="pl-c">//</span> "Hi\\n5!"</span>

<span class="pl-c1">String</span>.<span class="pl-smi">raw</span><span class="pl-s"><span class="pl-pds">`</span>Hi<span
                class="pl-cce">\u000A</span>!<span class="pl-pds">`</span></span>;
<span class="pl-c"><span class="pl-c">//</span> 'Hi\\u000A!'</span></pre>
    </div>
    <p>如果原字符串的斜杠已经转义，那么<code>String.raw</code>不会做任何处理。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-smi">raw</span><span class="pl-s"><span class="pl-pds">`</span>Hi<span
            class="pl-cce">\\</span>n<span class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> "Hi\\n"</span></pre>
    </div>
    <p><code>String.raw</code>的代码基本如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-en">raw</span> <span class="pl-k">=</span> <span class="pl-k">function</span> (<span
            class="pl-smi">strings</span>, <span class="pl-k">...</span><span class="pl-v">values</span>) {
  <span class="pl-k">let</span> output <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span><span
                class="pl-pds">"</span></span>;
  <span class="pl-k">let</span> index;
  <span class="pl-k">for</span> (index <span class="pl-k">=</span> <span class="pl-c1">0</span>; index <span
                class="pl-k">&lt;</span> <span class="pl-smi">values</span>.<span
                class="pl-c1">length</span>; index<span class="pl-k">++</span>) {
    output <span class="pl-k">+=</span> <span class="pl-smi">strings</span>.<span class="pl-smi">raw</span>[index] <span
                class="pl-k">+</span> values[index];
  }

  output <span class="pl-k">+=</span> <span class="pl-smi">strings</span>.<span class="pl-smi">raw</span>[index]
  <span class="pl-k">return</span> output;
}</pre>
    </div>
    <p><code>String.raw</code>方法可以作为处理模板字符串的基本方法，它会将所有变量替换，而且对斜杠进行转义，方便下一步作为字符串来使用。</p>
    <p><code>String.raw</code>方法也可以作为正常的函数使用。这时，它的第一个参数，应该是一个具有<code>raw</code>属性的对象，且<code>raw</code>属性的值应该是一个数组。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span class="pl-en">raw</span>({ raw<span
            class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>test<span
            class="pl-pds">'</span></span> }, <span class="pl-c1">0</span>, <span class="pl-c1">1</span>, <span
            class="pl-c1">2</span>);
<span class="pl-c"><span class="pl-c">//</span> 't0e1s2t'</span>

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-c1">String</span>.<span class="pl-en">raw</span>({ raw<span class="pl-k">:</span> [<span
                class="pl-s"><span class="pl-pds">'</span>t<span class="pl-pds">'</span></span>,<span class="pl-s"><span
                class="pl-pds">'</span>e<span class="pl-pds">'</span></span>,<span class="pl-s"><span
                class="pl-pds">'</span>s<span class="pl-pds">'</span></span>,<span class="pl-s"><span
                class="pl-pds">'</span>t<span class="pl-pds">'</span></span>] }, <span class="pl-c1">0</span>, <span
                class="pl-c1">1</span>, <span class="pl-c1">2</span>);</pre>
    </div>
    <h2><a href="#模板字符串的限制"  class="anchor" id="user-content-模板字符串的限制">

    </a>模板字符串的限制
    </h2>
    <p>前面提到标签模板里面，可以内嵌其他语言。但是，模板字符串默认会将字符串转义，导致无法嵌入其他语言。</p>
    <p>举例来说，标签模板里面可以嵌入 LaTEX 语言。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">latex</span>(<span
            class="pl-smi">strings</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}

<span class="pl-k">let</span> <span class="pl-c1">document</span> <span class="pl-k">=</span> latex<span
                class="pl-s"><span class="pl-pds">`</span></span>
<span class="pl-s"><span class="pl-cce">\n</span>ewcommand{<span class="pl-cce">\f</span>un}{<span
        class="pl-cce">\t</span>extbf{Fun!}}  // 正常工作</span>
<span class="pl-s"><span class="pl-cce">\n</span>ewcommand{<span class="pl-ii">\unicode}{\textbf{Unicode!}} // 报错</span></span>
<span class="pl-s"><span class="pl-cce">\n</span>ewcommand{<span class="pl-cce">\x</span>erxes}{<span
        class="pl-cce">\t</span>extbf{King!}} // 报错</span>
<span class="pl-s"></span>
<span class="pl-s">Breve over the h goes <span class="pl-ii">\u{h}ere // 报错</span></span>
<span class="pl-s"><span class="pl-pds">`</span></span></pre>
    </div>
    <p>上面代码中，变量<code>document</code>内嵌的模板字符串，对于 LaTEX 语言来说完全是合法的，但是 JavaScript 引擎会报错。原因就在于字符串的转义。</p>
    <p>模板字符串会将<code>\u00FF</code>和<code>\u{42}</code>当作 Unicode 字符进行转义，所以<code>\unicode</code>解析时报错；而<code>\x56</code>会被当作十六进制字符串转义，所以<code>\xerxes</code>会报错。也就是说，<code>\u</code>和<code>\x</code>在
        LaTEX 里面有特殊含义，但是 JavaScript 将它们转义了。</p>
    <p>为了解决这个问题，现在有一个<a href="https://tc39.github.io/proposal-template-literal-revision/">提案</a>，放松对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义，就返回<code>undefined</code>，而不是报错，并且从<code>raw</code>属性上面可以得到原始字符串。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">tag</span>(<span class="pl-smi">strs</span>) {
  strs[<span class="pl-c1">0</span>] <span class="pl-k">===</span> <span class="pl-c1">undefined</span>
  <span class="pl-smi">strs</span>.<span class="pl-smi">raw</span>[<span class="pl-c1">0</span>] <span
                class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\\</span>unicode and <span
                class="pl-cce">\\</span>u{55}<span class="pl-pds">"</span></span>;
}
tag<span class="pl-s"><span class="pl-pds">`</span><span class="pl-ii">\unicode and \u{55}`</span></span></pre>
    </div>
    <p>上面代码中，模板字符串原本是应该报错的，但是由于放松了对字符串转义的限制，所以不报错了，JavaScript 引擎将第一个字符设置为<code>undefined</code>，但是<code>raw</code>属性依然可以得到原始字符串，因此<code>tag</code>函数还是可以对原字符串进行处理。
    </p>
    <p>注意，这种对字符串转义的放松，只在标签模板解析字符串时生效，不是标签模板的场合，依然会报错。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">let</span> bad <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">`</span>bad escape sequence: <span class="pl-ii">\unicode`; // 报错</span></span></pre>
    </div>
</div>
</body>
</html>
